﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NUnit.Framework;
using ReactiveGraph.Core;
using ReactiveGraph.Tests.Containers;

namespace ReactiveGraph.Tests
{
    [TestFixture]
    [Explicit]
    public class PerformanceTests
    {
        [Test]
        [Description("33ms")]
        public void ReactiveNodeResolution()
        {
            var root = new DefaultTestContainer();
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (var i = 0; i < 5000; i++)
            {
                root.GetNode<ReactiveNode>();
            }
            Console.WriteLine("Create node " + stopwatch.ElapsedMilliseconds);
        }

        [Test]
        public void BaselinePocoNodes()
        {
            var root = new PocoNode();
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            CreateBigPocoTree(root,5000);
            Console.WriteLine("Poco nodes " + stopwatch.ElapsedMilliseconds);
        }

        private void CreateBigPocoTree(PocoNode root, decimal amount)
        {
            if (amount <= 0) return;
            var n1 = new PocoNode();
            var n2 = new PocoNode();
            root.Children.Add(n1);
            amount--;
            if (amount <= 0) return;
            root.Children.Add(n2);
            amount--;
            if (amount <= 0) return;

            CreateBigPocoTree(n1, amount / 2);
            CreateBigPocoTree(n2, amount / 2);


        }

      
        [Test]
        [Description("628ms")]
        public void CreateBigReactiveNodeTreeWithUnity()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var unity = new UnityTestContainer();
            var root2 = unity.GetNode<ReactiveNode>();
            CreateBigReativeNodeTree(root2,unity,  5000);
            Console.WriteLine("Unity " + stopwatch.ElapsedMilliseconds);
        }

        [Test]
        [Description("246ms")]
        public void CreateBigReactiveNodeTreeWithAutofac()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            var autoFac = new AutofacTestContainer();
            var root2 = autoFac.GetNode<ReactiveNode>();
            CreateBigReativeNodeTree(root2,autoFac,  5000);
            Console.WriteLine("AutoFac " + stopwatch.ElapsedMilliseconds);
        }


        [Test]
        [Description("2097ms")]
        public void SubscribeToBigTreeNodeEvents()
        {
            var autoFac = new AutofacTestContainer();
            var root = autoFac.GetNode<ReactiveNode>();
            CreateBigReativeNodeTree(root, autoFac, 5000);
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            root.Events.Subscribe();
            Console.WriteLine("1st Subscribe " + stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            root.Events.Subscribe();
            Console.WriteLine("2nd Subscribe " + stopwatch.ElapsedMilliseconds);
            stopwatch.Restart();
            root.ChildNodes.First().Events.Subscribe();
            Console.WriteLine("3rd Subscribe to first child " + stopwatch.ElapsedMilliseconds);
        }

        private void CreateBigReativeNodeTree(IReactiveNode root, IReactiveNodeContainer container, decimal amount)
        {
            if (amount <= 0) return;
            var n1 = container.GetNode<ReactiveNode>();
            root.ChildNodes.Add(n1);
            n1.AttachProperty(new Property<string>("Prop1"));
            amount--;
            if (amount <= 0) return;
            var n2 = container.GetNode<ReactiveNode>();
            root.ChildNodes.Add(n2);
            n2.AttachProperty(new Property<string>("Prop1"));
            amount--;
            if (amount <= 0) return;
            CreateBigReativeNodeTree(n1,container , amount / 2);
            CreateBigReativeNodeTree(n2,container , amount / 2);
        }



        class PocoNode
        {
            public PocoNode()
            {
                Children = new List<PocoNode>();
            }

            public List<PocoNode> Children { get; private set; }
            public string Property { get; set; }

            public int Count
            {
                get
                {
                    if (Children.Count == 0) return 0;

                    return Children.Count + Children.Sum(node => node.Count);
                }
            }
        }
    }
}
